package zdream.pmfield.data.foe;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Random;

import static zdream.pmfield.data.foe.FoeTemplate.V_STAT_HALF_VALUE;

/**
 * @author Zdream
 * @date 2023-11-09
 * @since 0.0.1
 */
public class Foe {
	public final transient FoeTemplate template;
	public short level;
	public String name;

	public short[] bStats = new short[BStat.values().length];
	public short[] vStats = new short[VStat.values().length];
	public short currentHp;
	/**
	 * 护盾值在每局开始时补满, 这里是最大值.
	 */
	public short maxShield;

	public final List<MoveTemplate> moves = new ArrayList<>();

	/**
	 * 所有被动技能 (包括特性以及像中毒一样的异常状态)
	 */
	public final List<Ability> abilities = new ArrayList<>();

	private Foe(FoeTemplate template) {
		this.template = Objects.requireNonNull(template);
	}

	public String getName() {
		return name == null ? template.name : name;
	}

	public int getBStat(BStat bStat) {
		return this.bStats[bStat.ordinal()];
	}

	public int getVStat(VStat vStat) {
		return this.vStats[vStat.ordinal()];
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder(100);
		appendStringWithoutHp(builder);

		builder.append(' ');
		builder.append("HP:").append(currentHp).append('/').append(bStats[BStat.HP.ordinal()]);
		builder.append(' ').append('+').append(maxShield);

		return builder.toString();
	}

	public void appendStringWithoutHp(StringBuilder builder) {
		builder.append('#').append(template.id);
		if (template.subId != 0) {
			builder.append('(').append(template.subId).append(')');
		}

		builder.append(' ');
		if (name == null) {
			builder.append(template.name);
		} else {
			builder.append(name).append('(').append(template.name).append(')');
		}

		builder.append(' ').append("Lv.").append(level);
	}

	public FoeBuilder updater() {
		return new FoeBuilder(this);
	}

	public static FoeBuilder builder(FoeTemplate template) {
		return new FoeBuilder(template);
	}

	public static class FoeBuilder {
		private final Foe foe;
		private boolean needRefreshVStat;
		private boolean needFullHp;
		/**
		 * 仅在 needRefreshVStat=true 时有效
		 */
		private boolean needSkipShuffleVStat;

		private FoeBuilder(FoeTemplate template) {
			foe = new Foe(template);
			foe.level = 1;
			needRefreshVStat = true;
			needFullHp = true;
		}

		private FoeBuilder(Foe foe) {
			this.foe = foe;
		}

		public FoeBuilder withLevel(int level) {
			foe.level = (short) level;
			return this;
		}

		public FoeBuilder withMove(MoveTemplate move) {
			foe.moves.add(move);
			return this;
		}

		public FoeBuilder withAbilityScripts(String... properties) {
			return withAbility(Ability.createCustomize(properties));
		}

		public FoeBuilder withAbility(Ability ability) {
			foe.abilities.add(ability);
			return this;
		}

		public FoeBuilder skipShuffleVStat() {
			needSkipShuffleVStat = true;
			return this;
		}

		private void initVStat() {
			final short[] array = foe.vStats;
			System.arraycopy(foe.template.vStats, 0, array, 0, array.length);

			if (needSkipShuffleVStat) {
				return;
			}

			final int length = array.length - 1; // "Luck" is not changed
			final Random random = new Random();
			for (int i = 0; i < 32; i++) {
				for (int index = 0; index < length; index++) {
					int to = random.nextInt(length);
					array[index] --;
					array[to] ++;
				}
			}
		}

		private void initBStat() {
			final short[] array = foe.bStats;
			final FoeTemplate template = foe.template;

			for (int i = 0; i < array.length; i++) {
				if (i == BStat.HP.ordinal()) {
					array[i] = calculateHpStat(template);
				} else {
					array[i] = calculateNotHpStat(template, BStat.values()[i]);
				}
			}
		}

		private short calculateHpStat(FoeTemplate template) {
			BStat bStat = BStat.HP;
			final short value = template.bStats[bStat.ordinal()];
			if (value == 1) {
				return 1;
			}

			return (short) ((value * 2) * foe.level / 100f + 10 + foe.level);
		}

		private short calculateNotHpStat(FoeTemplate template, BStat bStat) {
			final short value = template.bStats[bStat.ordinal()];

			final short major = foe.vStats[bStat.majorEffect.ordinal()];
			final short minor = foe.vStats[bStat.minorEffect.ordinal()];
			int factor = Math.max(major - V_STAT_HALF_VALUE, 0) * 3;
			factor += Math.max(minor - V_STAT_HALF_VALUE, 0);

			return (short) ((value * 2 + factor) * foe.level / 100f + 5);
		}

		private void initShield() {
			if (foe.level < 5) {
				foe.maxShield = 0;
				return;
			}

			float factorVStat = 40;
			for (int i = 0; i < VStat.SHIELD_FACTORS.length; i++) {
				factorVStat += Math.max(0, foe.vStats[i] - V_STAT_HALF_VALUE) * VStat.SHIELD_FACTORS[i];
			}

			float factorBStat = 0.1f;
			for (int i = 0; i < BStat.SHIELD_FACTORS.length; i++) {
				int bStat = foe.template.bStats[i] - 40;
				if (bStat <= 0) {
					continue;
				}
				bStat = bStat > 40 ? 20 + bStat / 2 : bStat;
				factorBStat += bStat * BStat.SHIELD_FACTORS[i];
			}

			foe.maxShield = (short) (factorVStat * factorBStat / 160 * foe.level);
		}

		public Foe build() {
			if (needRefreshVStat) {
				initVStat();
				needRefreshVStat = false;
			}
			initBStat();
			initShield();
			if (needFullHp) {
				foe.currentHp = foe.bStats[BStat.HP.ordinal()];
			}
			return foe;
		}
	}
}
